
    /* Parses a SPARQL result set, either
     *  - XML (http://www.w3.org/TR/rdf-sparql-XMLres/) or
     *  - JSON (http://www.w3.org/TR/rdf-sparql-json-res/)
     * into "Google JSON"
     * (see https://developers.google.com/chart/interactive/docs/reference#DataTable)
     * which is the JSON format that the DataTable object accepts.
     */

    // variable notation: xtable, xcol(s), xrow(s) -- x is 's'(parql) or 'g'(oogle).

    sgvizler.parser = (function () {

        ///////////////////////////////////////////////////
        // PRIVATEs

        // Converts results values into Google JSON values according
        // to the Google JSON datatype, i.e., values other than
        // strings and booleans need special treatment. If the value
        // is an URL, we "prefixify" it.
        var getGoogleJsonValue = function (value, gdatatype, stype) {
                var newvalue;
                if (gdatatype === 'number') {
                    newvalue = Number(value);
                } else if (gdatatype === 'date') {
                    //assume format yyyy-MM-dd
                    newvalue = new Date(value.substr(0, 4),
                                        value.substr(5, 2),
                                        value.substr(8, 2));
                } else if (gdatatype === 'datetime') {
                    //assume format yyyy-MM-ddZHH:mm:ss
                    newvalue = new Date(value.substr(0, 4),
                                        value.substr(5, 2),
                                        value.substr(8, 2),
                                        value.substr(11, 2),
                                        value.substr(14, 2),
                                        value.substr(17, 2));
                } else if (gdatatype === 'timeofday') {
                    //assume format HH:mm:ss
                    newvalue = [value.substr(0, 2),
                                value.substr(3, 2),
                                value.substr(6, 2)];
                } else { // datatype === 'string' || datatype === 'boolean'
                    if (stype === 'uri') { // replace namespace with prefix
                        newvalue = sgvizler.parser.prefixify(value);
                    }
                    newvalue = value;
                }
                return newvalue;
            },
            // Converts XSD datatypes into Google JSON datatypes.
            getGoogleJsonDatatype = function (sdatatype) {
                var convert = function (sdatatype) {
                    var xsdns = sgvizler.option.namespace.xsd,
                        cTable = [];
                    cTable[xsdns + "float"]    = 'number';
                    cTable[xsdns + "decimal"]  = 'number';
                    cTable[xsdns + "int"]      = 'number';
                    cTable[xsdns + "integer"]  = 'number';
                    cTable[xsdns + "long"]     = 'number';
                    cTable[xsdns + "boolean"]  = 'boolean';
                    cTable[xsdns + "date"]     = 'date';
                    cTable[xsdns + "dateTime"] = 'datetime';
                    cTable[xsdns + "time"]     = 'timeofday';
                    return cTable[sdatatype];
                };
                return convert(sdatatype) || 'string';
            };

        ///////////////////////////////////////////////////
        // PUBLICs

        return {

            convertSparqlXML2GoogleJSON: function (sxml) {
                var c, // column index.
                    r, // row index.
                    gcols = [],
                    grows = [],
                    gdatatype = [], // for easy reference of datatypes.
                    sresults = $(sxml).find('sparql').find('results').find('result');

                // Build gcols: find column names and datatypes.
                c = 0;
                $(sxml).find('sparql').find('head').find('variable').each(function () {
                    var sdatatype = null,
                        name = $(this).attr('name'),
                        scell = null,
                        scells = $(sresults).find('binding[name="' + name + '"]');
                    if (scells.length) {
                        scell = $(scells).first().children().first()[0]; // uri, literal element
                        sdatatype = $(scell).attr('datatype');
                    }
                    gdatatype[c] = getGoogleJsonDatatype(sdatatype);
                    gcols[c] = { id: name, label: name, type: gdatatype[c] };
                    c += 1;
                });

                // Build grows: get results.
                r = 0;
                $(sresults).each(function () {
                    var gvalue,
                        scells,
                        scell,
                        stype,
                        svalue,
                        grow = [];
                    for (c = 0; c < gcols.length; c += 1) {
                        gvalue = null;
                        scells = $(this).find('binding[name="' + gcols[c].id + '"]');
                        if (scells.length &&
                                $(scells).first().children().first() &&
                                $(scells).first().children().first()[0]) {
                            scell = $(scells).first().children().first()[0]; // uri, literal element
                            stype = scell.nodeName;
                            svalue = $(scell).first().text();
                            gvalue = getGoogleJsonValue(svalue, gdatatype[c], stype);
                        }
                        grow[c] = { v: gvalue };
                    }
                    grows[r] = { c: grow };
                    r += 1;
                });

                return { cols: gcols, rows: grows };
            },

            convertSparqlJSON2GoogleJSON: function (stable) {
                var c, // column index.
                    r, // row index.
                    srow,
                    grow,
                    gvalue,
                    sdatatype,
                    gcols = [],
                    grows = [],
                    gdatatype = [], // for easy reference of datatypes
                    scols = stable.head.vars,
                    srows = stable.results.bindings;

                // Build gcols: find column names and datatypes.
                for (c = 0; c < scols.length; c += 1) {
                    // Find a row where there is a value for this column
                    // in order to determine correct datatype.
                    r = 0;
                    while (srows[r][scols[c]] === undefined && r + 1 < srows.length) {
                        r += 1;
                    }
                    sdatatype = (srows[r][scols[c]] && srows[r][scols[c]].datatype) || null;
                    gdatatype[c] = getGoogleJsonDatatype(sdatatype);
                    gcols[c] = { id: scols[c], label: scols[c], type: gdatatype[c] };
                }

                // Build grows.
                // loop rows
                for (r = 0; r < srows.length; r += 1) {
                    srow = srows[r];
                    grow = [];
                    // loop cells
                    for (c = 0; c < scols.length; c += 1) {
                        gvalue = null;
                        if (srow[scols[c]] && srow[scols[c]].value) {
                            gvalue = getGoogleJsonValue(srow[scols[c]].value, gdatatype[c], srow[scols[c]].type);
                        }
                        grow[c] = { v: gvalue };
                    }
                    grows[r] = { c: grow };
                }

                return { cols: gcols, rows: grows };
            },

            // Returns number of results, SPARQL XML.
            countRowsSparqlXML: function (sxml) {
                return $(sxml).find('sparql').find('results').find('result').length;
            },

            // Returns number of results, SPARQL JSON.
            countRowsSparqlJSON: function (stable) {
                return stable.results.bindings && stable.results.bindings.length;
            },

            // Replaces namespaces with prefixes set in sgvizler.option.namespace.
            prefixify: function (url) {
                var ns;
                if (typeof url === 'string') {
                    for (ns in sgvizler.option.namespace) {
                        if (sgvizler.option.namespace.hasOwnProperty(ns) &&
                                sgvizler.fn.startsWith(url, sgvizler.option.namespace[ns])) {
                            return url.replace(sgvizler.option.namespace[ns], ns + ":");
                        }
                    }
                }
                return url;
            },
            // Replaces prefixes with namespaces set in sgvizler.option.namespace.
            unprefixify: function (qname) {
                var ns;
                if (typeof qname === 'string') {
                    for (ns in sgvizler.option.namespace) {
                        if (sgvizler.option.namespace.hasOwnProperty(ns) &&
                                sgvizler.fn.startsWith(qname, ns + ":")) {
                            return qname.replace(ns + ":", sgvizler.option.namespace[ns]);
                        }
                    }
                }
                return qname;
            }
        };

    }());

